﻿package render3d.adapters 
{
	import away3d.cameras.Camera3D;
	import away3d.cameras.lenses.PerspectiveLens;
	import away3d.containers.Scene3D;
	import away3d.containers.View3D;
	import away3d.core.base.Object3D;
	import away3d.core.clip.Clipping;
	import away3d.core.clip.FrustumClipping;
	import away3d.materials.BitmapMaterial;
	import away3d.primitives.Plane;
	import away3d.primitives.Skybox;
	import away3d.sprites.Sprite2D;
	import flash.display.BitmapData;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	import render3d.adapters.abstract.Render3DAdapterAbstract;
	import render3d.CrossEngineObject3D;
	import render3d.interfaces.IRender3DAdapter;
	import render3d.interfaces.IRender3DForObject3D;
	
	/**
	 * Away3D 3.5.0 Adapter
	 * @author Exey Panteleev
	 */
	public class Away3DAdapter extends Render3DAdapterAbstract implements IRender3DAdapter, IRender3DForObject3D
	{
		private var scene:Scene3D;
		private var camera:Camera3D;
		private var view:View3D;
		private var clipping:Clipping;
		
		public function Away3DAdapter() 
		{
			initScene();
		}
		
		public function initScene():void 
		{
			scene = new Scene3D();
			clipping = new FrustumClipping({minZ:1});
			camera = new Camera3D();
			camera.focus = 30;
			camera.lens = new PerspectiveLens();
			view = new View3D( { scene:scene, camera:camera, clipping:clipping } );
			viewContainer.addChild( view );
		}
		
		//--------------------------------------------------------------------------
		//
		//  Camera 
		//
		//--------------------------------------------------------------------------		
		
		public function setCameraPosition(x:Number = NaN, y:Number = NaN, z:Number = NaN):void {
			if (!isNaN(x)) camera.x = x;
			if (!isNaN(y)) camera.y = y;
			if (!isNaN(z)) camera.z = z;
		}		
		
		public function moveCamera(forwardSpeed:Number, rightSpeed:Number, jumpSpeed:Number):void {
			camera.x += rightSpeed;
			camera.y -= forwardSpeed;
			camera.z += jumpSpeed;
		}
		
		public function rotateCamera(x:Number = NaN, y:Number = NaN, z:Number = NaN):void {
			if (!isNaN(x)) camera.rotationX = x;
			if (!isNaN(y)) camera.rotationY = y;
			if (!isNaN(z)) camera.rotationZ = z;
		}		
		
		public function lookCamera(mouseX:Number, mouseY:Number):void {
			camera.rotationX -= mouseY;
			camera.rotationZ += mouseX;
		}		
		
		public function getCameraTransformation():Matrix3D 
		{
			var matrix:Matrix3D = new Matrix3D();
			var components:Vector.<Vector3D> = new Vector.<Vector3D>();
			components[0] = new Vector3D(camera.x, camera.y, camera.z);
			components[1] = new Vector3D(camera.rotationX * toRADIANS, camera.rotationY * toRADIANS, camera.rotationZ * toRADIANS);
			components[2] = new Vector3D(1, 1, 1);
			matrix.recompose(components);
			return matrix;
		}
		
		public function getCameraHeight():Number {
			return camera.z;
		}
		
		public function getCameraPanAngle():Number {
			return -camera.rotationZ*toRADIANS;
		}
		
		//--------------------------------------------------------------------------
		//
		//  3D Objects 
		//
		//--------------------------------------------------------------------------		
		
		public function createPlane(bitmapData:BitmapData, width:int, height:int, type:String = ""):CrossEngineObject3D
		{
			var material:BitmapMaterial = new BitmapMaterial(bitmapData);
			var plane:Plane = new Plane( { material:material, width:width, height:height, segmentsW:1, segmentsH:1 } );
			plane.rotationX = -90
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, plane);
			return crossEngineObject3D;
		}
		
		public function createSkybox(backTexture:BitmapData, leftTexture:BitmapData, frontTexture:BitmapData, rightTexture:BitmapData, topTexture:BitmapData, bottomTexture:BitmapData):CrossEngineObject3D
		{
			var backMaterial:BitmapMaterial = new BitmapMaterial(frontTexture); // swapped
			var leftMaterial:BitmapMaterial = new BitmapMaterial(rightTexture); // swapped
			var frontMaterial:BitmapMaterial = new BitmapMaterial(backTexture); // swapped
			var rightMaterial:BitmapMaterial = new BitmapMaterial(leftTexture); // swapped
			var topMaterial:BitmapMaterial = new BitmapMaterial(topTexture);
			var bottomMaterial:BitmapMaterial = new BitmapMaterial(bottomTexture);
			var skybox:Skybox = new Skybox( frontMaterial, leftMaterial, backMaterial, rightMaterial, topMaterial, bottomMaterial );
			skybox.rotationX = -90;
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, skybox);
			return crossEngineObject3D;
		}
		
		public function createSprite3D(bitmapData:BitmapData, type:String = ""):CrossEngineObject3D 
		{
			var sprite3D:Sprite2D = new Sprite2D(bitmapData);
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, sprite3D);
			return crossEngineObject3D;	
		}
		
		//--------------------------------------------------------------------------
		//
		//  Common
		//
		//--------------------------------------------------------------------------
		
		public function update():void 
		{
			view.render();			
		}
		
		public function resize(stageWidth:int, stageHeight:int):void 
		{
			view.x = stageWidth/2;
			view.y = stageHeight/2;
		}
		
		//--------------------------------------------------------------------------
		//
		//  INTERFACE render3d.interfaces.IRender3DForObject3D
		//
		//--------------------------------------------------------------------------
		
		public function copyTransformation(sourceMatrix3D:Matrix3D, targetObject3D:*):void 
		{
			var components:Vector.<Vector3D> = sourceMatrix3D.decompose();
			Object3D(targetObject3D).x = components[0].x;
			Object3D(targetObject3D).y = components[0].y;
			Object3D(targetObject3D).z = components[0].z;
			
			Object3D(targetObject3D).rotationX = components[1].x*toDEGREES;
			Object3D(targetObject3D).rotationY = components[1].y*toDEGREES;
			Object3D(targetObject3D).rotationZ = components[1].z*toDEGREES + 180;
			
			Object3D(targetObject3D).scaleX = components[2].x;
			Object3D(targetObject3D).scaleY = components[2].y;
			Object3D(targetObject3D).scaleZ = components[2].z;
		}
		
		public function copyPosition(sourceMatrix3D:Matrix3D, targetObject3D:*):void 
		{
			var components:Vector.<Vector3D> = sourceMatrix3D.decompose();
			Object3D(targetObject3D).x = components[0].x;
			Object3D(targetObject3D).y = components[0].y;
			Object3D(targetObject3D).z = components[0].z;
		}
		
		override public function rotateAxis( matrix3D:Matrix3D, vector3D:Vector3D ):void 
		{
			var vx:Number = vector3D.x;
			var vy:Number = vector3D.y;
			var vz:Number = vector3D.z;

			// 00 01 02 03 
			// 04 05 06 07
			// 08 09 10 11
			// 12 13 14 15
			vector3D.x = -(vx * matrix3D.rawData[0] + vy * matrix3D.rawData[4] + vz * matrix3D.rawData[8]);
			vector3D.y = -(vx * matrix3D.rawData[1] + vy * matrix3D.rawData[5] + vz * matrix3D.rawData[9]);
			vector3D.z = vx * matrix3D.rawData[2] + vy * matrix3D.rawData[6] + vz * matrix3D.rawData[10];
			
			vector3D.normalize();
		}
		
		public function changeTexture( bitmapData:BitmapData, targetObject3D:* ):void 
		{
			if(targetObject3D is Sprite2D)
				Sprite2D(targetObject3D).bitmap = bitmapData;
		}		
		
		//--------------------------------------------------------------------------
		//
		//  INTERFACE render3d.interfaces.abstract.IRender3DAbstract
		//
		//--------------------------------------------------------------------------
		
		public function addChild(child:CrossEngineObject3D):void
		{
			scene.addChild(child.object3D);
		}
		
		public function removeChild(child:CrossEngineObject3D):void
		{
			scene.removeChild(child.object3D);
		}
	}
}